<template>
	<view class="slider-range-body" :class="{
			disabled: disabled,
			padding: tips || scaleInfo.show
		}">

		<!-- 刻度线 -->
		<view class="scale-bar" v-if="scaleInfo.show">
			<view class="scale-item" v-for="(scaleItem, scaleIndex) in scaleLine" :key="scaleIndex" :style="{
					left: scaleItem.left,
					color: scaleItem.color,
					fontSize: scaleItem.fontSize,
				}">
				{{ scaleItem.label }}
				<view class="scale-item-line" :style="{
						backgroundColor: scaleItem.tickColor
					}" />
			</view>
		</view>

		<!-- 滑动提示 -->
		<view class="tips-bar" v-if="tips">
			<view class="tips-item" v-if="dragging == 'firstBlock'" :style="{
					left: getFirstBlockPosition,
					zIndex: firstBlock.zIndex
				}">
				<!-- #ifndef MP-WEIXIN -->
				<slot name="tips" :value="firstValue">
					<view class="default-tips">{{ firstValue }}</view>
				</slot>
				<!-- #endif -->
				<!-- #ifdef MP-WEIXIN -->
				<slot name="firstTips">
					<view class="default-tips">{{ firstValue }}</view>
				</slot>
				<!-- #endif -->
			</view>
			<view class="tips-item" v-if="dragging == 'secondBlock'" :style="{
					left: getSecondBlockPosition,
					zIndex: secondBlock.zIndex
				}">
				<!-- #ifndef MP-WEIXIN -->
				<slot name="tips" :value="secondValue">
					<view class="default-tips">{{ secondValue }}</view>
				</slot>
				<!-- #endif -->
				<!-- #ifdef MP-WEIXIN -->
				<slot name="secondTips">
					<view class="default-tips">{{ secondValue }}</view>
				</slot>
				<!-- #endif -->
			</view>
		</view>

		<!-- 滑动条 -->
		<view class="slider-bar" :style="[getBackgroundStyle]">
			<view class="slider-active-bar" :style="[getActiveStyle]"></view>
		</view>

		<!-- 滑块 -->
		<view class="block-bar">
			<view class="block-item" v-if="firstBlock.show" :style="{
					left: getFirstBlockPosition,
					zIndex: firstBlock.zIndex
				}" data-sort="firstBlock" @touchstart.stop.prevent="onTouchStart" @touchmove.stop.prevent="onTouchMove"
				@touchend.stop.prevent="onTouchEnd">
				<!-- #ifndef MP-WEIXIN -->
				<slot name="block">
					<view class="default-block block-font"></view>
				</slot>
				<!-- #endif -->
				<!-- #ifdef MP-WEIXIN -->
				<slot name="firstBlock">
					<view class="default-block block-font"></view>
				</slot>
				<!-- #endif -->
			</view>
			<view class="block-item" v-if="secondBlock.show" :style="{
					left: getSecondBlockPosition,
					zIndex: secondBlock.zIndex
				}" data-sort="secondBlock" @touchstart.stop.prevent="onTouchStart" @touchmove.stop.prevent="onTouchMove"
				@touchend.stop.prevent="onTouchEnd">
				<!-- #ifndef MP-WEIXIN -->
				<slot name="block">
					<view class="default-block block-font"></view>
				</slot>
				<!-- #endif -->
				<!-- #ifdef MP-WEIXIN -->
				<slot name="secondBlock">
					<view class="default-block block-font"></view>
				</slot>
				<!-- #endif -->
			</view>
			<!-- 这个元素用来撑高度 -->
			<view class="block-placeholder">
				<!-- #ifndef MP-WEIXIN -->
				<slot name="block">
					<view class="default-block block-font"></view>
				</slot>
				<!-- #endif -->
				<!-- #ifdef MP-WEIXIN -->
				<slot name="placeholderBlock">
					<view class="default-block block-font"></view>
				</slot>
				<!-- #endif -->
			</view>
		</view>

	</view>
</template>

<script>
	/**
	 * @description 区间滑动选择组件
	 * @example <slider-range></slider-range>
	 * @property {number|array<number>} value 初始值，为 number 时表示滑动选择器，为 array<number> 时表示区间滑动选择器
	 * @property {string} valueType 当 value 为 number 时，可滑动的是哪个值，可选值：min、max，默认 max
	 * @property {number} min 最小值（最左侧值）默认 0
	 * @property {number} max 最大值（最右侧值）默认 100
	 * @property {number} step 步长，为 0 时不设步长，默认为 1
	 * @property {number|string} height 滑动条高度，可自定义单位，默认单位 rpx
	 * @property {boolean} disabled 是否禁用，默认 false
	 * @property {boolean} tips 是否显示滑动提示，默认为 true
	 * @property {boolean|object} scale 刻度条配置
	 * @property {object} backgroundStyle 背景条自定义样式
	 * @property {object} activeStyle 选中条自定义样式
	 **/
	// 补全单位
	const completeUnit = (val = 0, unit = 'rpx') => {
		if (isNaN(Number(val))) return val
		return val + unit
	}
	// 乘法，防止小数计算失真
	const multiplication = (val1, val2) => {
		let dotIndex = String(val2).indexOf('.')
		if (dotIndex != -1) {
			let floatLength = String(val2).length - (dotIndex + 1)
			return val1 * (val2 * Math.pow(10, floatLength)) / Math.pow(10, floatLength)
		} else {
			return val1 * val2
		}
	}
	export default {
		name: "wjf-slider-rang",
		props: {
			value: {
				type: [Number, Array],
				default: () => []
			},
			valueType: {
				type: String,
				default: 'max'
			},
			min: {
				type: Number,
				default: 0
			},
			max: {
				type: Number,
				default: 100
			},
			step: {
				type: Number,
				default: 1
			},
			height: {
				type: [Number, String],
				default: 12
			},
			disabled: {
				type: Boolean,
				default: false
			},
			tips: {
				type: Boolean,
				default: true
			},
			scale: {
				type: [Boolean, Object],
				default: true
			},
			backgroundStyle: {
				type: Object,
				default: () => {
					return {
						width: '100%'
					}
				}
			},
			activeStyle: {
				type: Object,
				default: () => {
					return {}
				}
			}
		},
		data() {
			return {
				// 两个值
				firstValue: 0,
				secondValue: 0,
				// 两个滑点
				firstBlock: {
					show: true,
					zIndex: 1,
				},
				secondBlock: {
					show: true,
					zIndex: 2,
				},
				// 滑动中
				dragging: '',
				// 刻度线设置
				scaleInfo: {
					show: true,
					min: true,
					max: true,
					interval: 'auto',
					color: '#333',
					tickColor: '#999',
					fontSize: 22,
					format: null,
				},
				scaleLine: [],
			}
		},
		computed: {
			getStep() {
				if (this.max - this.min < this.step) return this.max - this.min
				return this.step
			},
			// 滑点坐标
			getFirstBlockPosition() {
				return (this.firstValue - this.min) / (this.max - this.min) * 100 + '%'
			},
			getSecondBlockPosition() {
				return (this.secondValue - this.min) / (this.max - this.min) * 100 + '%'
			},
			// 背景条样式
			getBackgroundStyle() {
				return {
					...this.backgroundStyle,
					height: completeUnit(this.height)
				}
			},
			// 选中条样式
			getActiveStyle() {
				let min = Math.min(this.firstValue, this.secondValue)
				let max = Math.max(this.firstValue, this.secondValue)
				return {
					...this.activeStyle,
					left: (min - this.min) / (this.max - this.min) * 100 + '%',
					width: (max - min) / (this.max - this.min) * 100 + '%',
				}
			}
		},
		mounted() {
			this.create()
		},
		methods: {
			create() {
				if (this.max <= this.min) {
					throw '[slider-range] max 属性不应小于或等于 min 属性'
				}
				if (typeof this.value == 'number') {
					let value = this.value
					if (this.value > this.max) value = this.max
					if (this.value < this.min) value = this.min
					if (this.valueType == 'max') {
						this.firstBlock.show = false
						this.firstValue = this.min
						this.secondValue = value
					} else {
						this.secondBlock.show = false
						this.secondValue = this.max
						this.firstValue = value
					}
				} else {
					let firstValue = this.value[0] || this.min
					if (firstValue > this.max) firstValue = this.max
					if (firstValue < this.min) firstValue = this.min
					let secondValue = this.value[1] || this.max
					if (secondValue > this.max) secondValue = this.max
					if (secondValue < this.min) secondValue = this.min
					this.firstValue = firstValue
					this.secondValue = secondValue
					this.firstBlock.show = true
					this.secondBlock.show = true
				}
				// 计算刻度值
				if (typeof this.scale != 'object') {
					this.scaleInfo.show = !!this.scale
				} else {
					this.scaleInfo = {
						...this.scaleInfo,
						...this.scale
					}
				}
				if (!this.scaleInfo.show) return
				let interval = this.scaleInfo.interval
				let step = this.getStep > 0 ? this.getStep : 1
				if (typeof interval != 'number') {
					interval = Math.ceil((this.max - this.min) / step / 10)
				} else {
					interval = interval + 1
				}
				let cumsum = this.min
				let arr = []
				while (cumsum <= this.max) {
					arr.push({
						value: cumsum,
						label: cumsum,
						color: this.scaleInfo.color,
						fontSize: completeUnit(this.scaleInfo.fontSize),
						tickColor: this.scaleInfo.tickColor,
						left: (cumsum - this.min) / (this.max - this.min) * 100 + '%'
					})
					cumsum = cumsum + multiplication(interval, step)
				}
				if (arr[0].value == this.min && !this.scaleInfo.min) arr = arr.slice(1)
				if (arr[arr.length - 1].value == this.max && !this.scaleInfo.max) arr = arr.slice(0, arr.length - 1)
				if (arr[arr.length - 1].value != this.max && this.scaleInfo.max) arr.push({
					value: this.max,
					label: this.max,
					color: this.scaleInfo.color,
					fontSize: completeUnit(this.scaleInfo.fontSize),
					tickColor: this.scaleInfo.tickColor,
					left: '100%'
				})
				let format = this.scaleInfo.format
				if (typeof format == 'function') {
					arr = arr.map((item, index) => {
						return format(item, index)
					})
				}
				this.scaleLine = [...arr]
				this.$forceUpdate()
			},
			onTouchStart(e) {

				if (this.disabled) return
				this.dragging = e.currentTarget.dataset.sort
				e.currentTarget.dataset.sort == 'firstBlock' ? this.firstBlock.zIndex = this.secondBlock.zIndex + 1 : this
					.secondBlock.zIndex = this.firstBlock.zIndex + 1
				this.startDragPostion = e.changedTouches ? e.changedTouches[0].pageX : e.pageX
				this.startValue = e.currentTarget.dataset.sort == 'firstBlock' ? this.firstValue : this.secondValue
				this.$emit('start', {
					block: e.currentTarget.dataset.sort,
					value: this.startValue,
					values: [Math.min(this.firstValue, this.secondValue), Math.max(this.firstValue, this
						.secondValue)]
				})
			},
			onTouchMove(e) {
				if (this.disabled) return

				// 获取当前滑块的位置
				// const currentPosition = e.changedTouches ? e.changedTouches[0].pageX : e.pageX;

				// 获取当前滑块的数据排序（firstBlock 或 secondBlock）
				const currentSort = e.currentTarget.dataset.sort;

				// 获取当前滑块对应的值
				const currentValue = currentSort === 'firstBlock' ? this.firstValue : this.secondValue;

				// 获取另一个滑块的值
				const otherValue = currentSort === 'firstBlock' ? this.secondValue : this.firstValue;

				// 计算两个滑块的间隔
				const interval = Math.abs(currentValue - otherValue);

				// 如果两个滑块处于相邻的位置
				if (interval <= this.getStep*2) {
					// 阻止当前滑块移动
					return;
				}

				// 其他情况下允许滑块移动
				this.onDrag(e)
			},
			onTouchEnd(e) {
				// 禁止滑块移动的条件是：两个滑块处于相邻的位置
				// if (Math.abs(this.firstValue - this.secondValue) <= (this.getStep * 2)) {
				// 	// 当滑块处于相邻位置时，禁止滑块移动
				// 	return;
				// }
				this.dragging = ''
				if (this.disabled) return
				this.onDrag(e, true)
			},
			onDrag(e, end = false) {
				let pageX = e.changedTouches ? e.changedTouches[0].pageX : e.pageX
				let view = uni.createSelectorQuery().in(this).select('.block-bar')
				view.boundingClientRect(data => {
					let diff = ((pageX - this.startDragPostion) / data.width) * (this.max - this.min)
					this.onUpdateValue(this.startValue + diff, e.currentTarget.dataset.sort, end)
				}).exec()
			},
			onUpdateValue(value, sort, end = false) {
				// 确保 value 在 min 和 max 范围内
				if (value < this.min) value = this.min
				if (value > this.max) value = this.max

				// 如果步长大于 0，则将 value 四舍五入到最接近的步长倍数
				if (this.getStep > 0 && value != this.min && value != this.max) {
					value = this.min + multiplication(Math.round((value - this.min) / this.getStep), this.getStep)
				} else {
					value = Number(value.toFixed(2))
				}

				// 根据滑块的排序，更新对应的值，并确保左侧滑块不会超过右侧滑块
				if (sort == 'firstBlock') {
					// this.firstValue = value
					// 确保左侧滑块不会超过右侧滑块的位置
					this.firstValue = Math.min(value, this.secondValue);
				} else {
					// this.secondValue = value
					// 确保右侧滑块不会小于左侧滑块的位置
					this.secondValue = Math.max(value, this.firstValue);
				}


				// 触发 change 事件，通知外部值的变化
				this.$emit('change', {
					firstValue: this.firstValue,
					secondValue: this.secondValue,
					values: [Math.min(this.firstValue, this.secondValue), Math.max(this.firstValue, this
						.secondValue)]
				})
				// 如果是结束滑动事件，触发 end 事件，通知外部滑动结束
				if (end) {
					this.$emit('end', {
						block: sort,
						value: value,
						values: [Math.min(this.firstValue, this.secondValue), Math.max(this.firstValue,
							this
							.secondValue)]
					})
				}
			}
		}
	}
</script>
<style lang="scss" scoped>
	@font-face {
		font-family: 'block-font';
		src: url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAK8AAsAAAAABnAAAAJvAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACCcApsgQgBNgIkAwgLBgAEIAWEZwcvG8MFyJ6aPCkSYgMLUMxWfTCChHj4WkPf39sgkItGUKiiTMrOVnmWnRpxV/v99mPaxBdpKnZP/N7cW6x5SV7/UAI0T6aVIZMZ2movuV6lLBp1KX/zBrwzj+/46Y16pUYVDyjxgOQB04slFMJMxEMeBfVaXwOQEc8I1FpWL+CwvXcUYt6WyIEkQezRIGuON11dgjCdlmVaqxYq0b1ZfFNBek+3wNfo8/FfcYSRVBTstpPXNgnOf+KdBYGb5SbFD5vz9dDHEipQYB7IxEV3+UigaJIC1YaDjNLOLPgX/18GIbFrv1B/nd1YD/qp4Z6MnulWawlua6JhYG1UGzHs5uiDw3/gSRZb18/XZ8qc05vx1z2uG3vcbsrWSqdLJiZKpkrvXDVUUzV/ljaCI8mt5X0x7WM8rmu/cb8rjlNrjKHW/BKOQCNAeVLsFYvgQ/Djd7pqIbb26roM/hrt71GyPMmARUH1fsPBT3I+gMRsZEn5WAY5nAyf11br5CjBjrf6Gutpu5l6qNY2IlqlL0Oh2iiVuVlUqLOKStUOUGtO2+E6bVyBItdg0g5BaOZH0ug7Cs0uqMw9o0Knb1Rq9odaVxF9Xp3JYC1CXiJZwuUN1Bl3FNdNkdZhWV2xlkRdYVwWnk5zWJRfWK51yI4sptjgrarFRAoqgtvYLh+TLYujK7ghM8rXiNzqggKl6S35jNvQQpAtITIJWrYB6RjOoQRkKn7fMJlqhWWJAKnxsuDRqXZUJF8hQHU4HGjgTl7xrFIVI0SBFAJnQ+16lsxi4ZDbPMsgY0g+bUTRVa3AISmoI399l/1px6CW7U3hTClUPFqcmQAAAA==') format('woff2');
	}

	.block-font {
		font-family: "block-font" !important;
		font-style: normal;
		-webkit-font-smoothing: antialiased;
		-moz-osx-font-smoothing: grayscale;

		&:before {
			content: "\e917";
		}
	}

	.slider-range-body {
		--main-color: #333333;
		--primary-color: #007aff;
		width: 100%;
		box-sizing: border-box;
		position: relative;

		&.disabled {
			opacity: .7;
		}

		&.padding {
			padding-top: 72rpx;
		}

		.slider-bar {
			width: 100%;
			height: 12rpx;
			background-color: #f8f8f8;
			border-radius: 999999rpx;
			overflow: hidden;
			position: relative;

			.slider-active-bar {
				height: 100%;
				background-color: var(--primary-color);
				border-radius: 999999rpx;
				position: absolute;
			}
		}

		.block-bar {
			width: 100%;
			margin-top: 8rpx;
			position: relative;

			.block-item {
				position: absolute;
				top: 0;
				left: 0;
				transform: translateX(-50%);
			}

			.block-placeholder {
				opacity: 0 !important;
				user-select: none;
				pointer-events: none;
			}

			.block-item,
			.block-placeholder {
				.default-block {
					color: var(--primary-color);
					font-size: 42rpx;
				}
			}
		}

		.tips-bar {
			width: 100%;
			height: 60rpx;
			position: absolute;
			top: 0;

			.tips-item {
				position: absolute;
				top: 0;
				left: 0;
				transform: translateX(-50%);

				.default-tips {
					width: 60rpx;
					height: 60rpx;
					display: flex;
					align-items: center;
					justify-content: center;
					border-radius: 50%;
					background-color: #ffffff;
					box-shadow: 0 0 20rpx rgba(0, 0, 0, .4);
					color: var(--main-color);
					font-size: 22rpx;
					font-weight: 500;
				}
			}
		}

		.scale-bar {
			width: 100%;
			height: 60rpx;
			position: absolute;
			top: 0;

			.scale-item {
				display: flex;
				flex-direction: column;
				align-items: center;
				color: #333333;
				font-size: 22rpx;
				position: absolute;
				bottom: 0;
				transform: translateX(-50%);

				.scale-item-line {
					width: 2rpx;
					height: 8rpx;
					background-color: #999999;
				}
			}
		}
	}
</style>